
import java.util.*;
import java.util.Map.Entry;

import javax.swing.text.html.parser.Element;

public class PopulationCalculator {

	private static int repStringLength = 11;
	public static double[][] chancesMatrix;
	private static final double SIG_LOWER_THRESHOLD = 0.000;
	
	/**
	 * Calculates the origins count for each snp of the supplied snp map 
	 * @param snps snp data
	 * @param personSnps a person snp map
	 * @return the person CEU origin count
	 */
	public static boolean[] calcCEUSource(HashMap<Integer, SNPData> snps, byte[] haplotype) {
		int first = -1;
		int last = first;
		boolean[] ret = new boolean[snps.size()];
		boolean result ;
		for (int i = 0 ; i < snps.size() - 1; i++) {
			if (first == -1) {
				first = i;
			}
			SNPData relevantSnp = snps.get(i), nextSnp = snps.get(i + 1);
			
			if (relevantSnp.getCluster() == nextSnp.getCluster()) {
				continue;
			} else {
				
				//get haplotype source estimation for cluster (first, i)
				markCluster(haplotype, snps, first, i, ret);		
				first = -1;
			}
		}
		//last iteration
		//get haplotype source estimation for cluster (first, snps.size() - 1)
		markCluster(haplotype, snps, first, snps.size() - 1, ret);
		
		return ret;
	}
	
	static public Map<Byte,Double> validateCluster(int clusterStartIndex, int clusterEndIndex, HashMap<Integer, SNPData> m_snps, List<PersonData> m_testCases, int personIndex)
	{
		byte[] SNPArray = m_testCases.get(personIndex).getSNPs();
		
		if (chancesMatrix == null)
		{
			chancesMatrix = new double[3][m_snps.size()];
			
			for (int i = 0; i < m_snps.size(); i++)
			{
				SNPData data = m_snps.get(i);
				double pCEU = data.getCEU();
				double pYRI = data.getYRI();
				int SNPNumAtPosition = SNPArray[i];
				if (SNPNumAtPosition == 0)
				{
					chancesMatrix[0][i] = (1 - pYRI) * (1 - pYRI);
					chancesMatrix[1][i] = (1 - pYRI) * (1 - pCEU);
					chancesMatrix[2][i] = (1 - pCEU) * (1 - pCEU);
				} else if (SNPNumAtPosition == 1)
				{
					chancesMatrix[0][i] = pYRI * (1 - pYRI);
					chancesMatrix[1][i] = pYRI * (1 - pCEU) + pCEU * (1 - pYRI);
					chancesMatrix[2][i] = pCEU * (1 - pCEU);
				} else if (SNPNumAtPosition == 2)
				{
					chancesMatrix[0][i] = pYRI * pYRI;
					chancesMatrix[1][i] = pYRI * pCEU;
					chancesMatrix[2][i] = pCEU * pCEU;
				}
			}
		}

		//byte[] SNPGuessesArray = m_testCases.get(personIndex).getResultSNPs();
		//byte[] currentGuesses = new byte[m_snps.size()];
		
		HashMap<Byte, Double> possibilitiesSet = new HashMap<Byte, Double>();
		possibilitiesSet.put((byte)0, 1.0);
		possibilitiesSet.put((byte)1, 1.0);
		possibilitiesSet.put((byte)2, 1.0);
		
		double oneChances = 1.0;
		double zeroChances = 1.0;
		double twoChances = 1.0;
		
		for (int i = clusterStartIndex; i < clusterEndIndex; i++)
		{
			zeroChances *= chancesMatrix[0][i];
			oneChances *= chancesMatrix[1][i];
			twoChances *= chancesMatrix[2][i];
			
			if (chancesMatrix[0][i] <= SIG_LOWER_THRESHOLD)
			{
				possibilitiesSet.remove((byte)0);
			}
			if (chancesMatrix[1][i] <= SIG_LOWER_THRESHOLD)
			{
				possibilitiesSet.remove((byte)1);
			}
			if (chancesMatrix[2][i] <= SIG_LOWER_THRESHOLD)
			{
				possibilitiesSet.remove((byte)2);
			}
		}
		
		for (Entry<Byte,Double> item : possibilitiesSet.entrySet()) 
		{
			if (item.getKey().equals(0))
				item.setValue(zeroChances);
			if (item.getKey().equals(1))
				item.setValue(oneChances);
			if (item.getKey().equals(2))
				item.setValue(twoChances);
		}
		
		return possibilitiesSet;
	}
	
	static void markCluster(byte[] haplotype, HashMap<Integer, SNPData> snps, int firstSnpIndex, int lastSnpIndex, boolean[] ret) {
		double pCEU = 1;
		double pYRI = 1;
		boolean result = false;
		for (int i = firstSnpIndex ; i <= lastSnpIndex  ; i++) {
			if (haplotype[i] != -1) {
				pCEU *= haplotype[i] == 1 ? snps.get(i).getCEU() : 1 - snps.get(i).getCEU();
				pYRI *= haplotype[i] == 1 ? snps.get(i).getYRI() : 1 - snps.get(i).getYRI();
			}
			if (pCEU < 0.0001 || pYRI  < 0.0001) 
			{
				pCEU *= 10000;
				pYRI *= 10000;
			}
			
		}
		if (pCEU > pYRI) {
			result = true;
		}
		for (int i = firstSnpIndex ; i <= lastSnpIndex ; i++) {
			ret[i] = result;
		}
	}
	
	/**
	 * Calculates the trivial solution
	 * @param allSnps all snps data
	 * @param personSnps a person snps data
	 * @return the CEU snps count for each snp
	 */
	private static byte[] trivialCEUSource(HashMap<Integer, SNPData> allSnps, byte[] personSnps) {
		byte[] ret = new byte[personSnps.length];
		
		for (int j = 0 ; j < ret.length ; j++) {
			SNPData snp = allSnps.get(j);	
			
			ret[j] =  snp.getCEU() >= snp.getYRI() ? personSnps[j] : 0;
		}
				
		return ret;
	}
	
	public static void getHaplotypesForFamily(byte[] parentA, byte[] parentB, byte[] child, HashMap<Integer, SNPData> snps, byte[][] haplotypes, int familyIndex) {
		
		int iddoCounter = 0;
		int aviaCounter = 0;
		if (parentA.length != parentB.length || parentA.length != child.length) {
			throw new IllegalArgumentException("length of arrays do not match");
		}
				
		//byte[][] ret = new byte[6][parentA.length];
		int childChrParentA = 4 + familyIndex * 6;
		int childChrParentB = 5 + familyIndex * 6;
		int parentAChrA = 0 + familyIndex * 6;
		int parentAChrB = 1 + familyIndex * 6;
		int parentBChrA = 2 + familyIndex * 6;
		int parentBChrB = 3 + familyIndex * 6;
		
		//initialization
		for (int i = 0 ; i < child.length ; i++) {
			haplotypes[parentAChrA][i] = -1;
			haplotypes[parentAChrB][i] = -1;
			haplotypes[parentBChrA][i] = -1;
			haplotypes[parentBChrB][i] = -1;
			haplotypes[childChrParentA][i] = -1;
			haplotypes[childChrParentB][i] = -1;
		}
		
		//first phase - gathering known data
		for (int i = 0 ; i < child.length ; i++) {
			if (parentA[i] == 0) {
				haplotypes[parentAChrA][i] = 0;
				haplotypes[parentAChrB][i] = 0;
			} else if (parentA[i] == 2) {
				haplotypes[parentAChrA][i] = 1;
				haplotypes[parentAChrB][i] = 1;
				haplotypes[childChrParentA][i] = 1;
				if (child[i] == 1) {
					haplotypes[childChrParentB][i] = 0;
				}
			}
			if (parentB[i] == 0) {
				haplotypes[parentBChrA][i] = 0;
				haplotypes[parentBChrB][i] = 0;
			} else if (parentB[i] == 2) {
				haplotypes[parentBChrA][i] = 1;
				haplotypes[parentBChrB][i] = 1;
				haplotypes[childChrParentB][i] = 1;
				if (child[i] == 1) {
					haplotypes[childChrParentA][i] = 0;
				}
			}			
			
			if (child[i] == 0) {
				haplotypes[childChrParentA][i] = 0;
				haplotypes[childChrParentB][i] = 0;
			} else if (child[i] == 2) {
				haplotypes[childChrParentA][i] = 1;
				haplotypes[childChrParentB][i] = 1;				
			} else { //child[i] == 1
				if (parentA[i] == 0) {
					haplotypes[childChrParentA][i] = 0;
					haplotypes[childChrParentB][i] = 1;
				} else if (parentB[i] == 0) {
					haplotypes[childChrParentA][i] = 1;
					haplotypes[childChrParentB][i] = 0;
				}
			}
			
			if (haplotypes[parentAChrA][i] == -1 && haplotypes[childChrParentA][i] != -1) {
				haplotypes[parentAChrA][i] = haplotypes[childChrParentA][i];
				haplotypes[parentAChrB][i] = (byte) (parentA[i] - haplotypes[parentAChrA][i]);
			}
			if (haplotypes[parentBChrA][i] == -1 && haplotypes[childChrParentB][i] != -1) {
				haplotypes[parentBChrA][i] = haplotypes[childChrParentB][i];
				haplotypes[parentBChrB][i] = (byte) (parentB[i] - haplotypes[parentBChrA][i]);
			}
		}	
	}
	
	public static void decideUnknownSnpsForChildren(byte[][] haplotypes, byte[][] pops) {
		//iterate over all offsprings
		for (int i = 0 ; i < haplotypes.length / 6 ; i++) {
			int line = 4 * i + 4;
			for (int j = 0 ; j < haplotypes[line].length ; j++) {
				//look for a problematic region
				if (haplotypes[line][j] == -1) {
					String repString = GetRepresentingString(haplotypes[line], j);
					//TODO : maybe continue if time allows
					System.out.println(repString);
				}
			}
		}
	}

	private static String GetRepresentingString(byte[] bs, int j) {

		String ret = null;
		StringBuilder builder = new StringBuilder();
		
		if (j < repStringLength / 2) {
			for (int i = 0 ; i < repStringLength ; i++) {
				builder.append(bs[i]);
			}
		} else if (bs.length - j - 1< repStringLength / 2) {
			for (int i = bs.length - repStringLength  ; i < bs.length ; i++) {
				builder.append(bs[i]);
			}
		} else {
			for (int i = 0 ; i < repStringLength ; i++ )
				builder.append(bs[i + j - repStringLength / 2]);
		}
		
		return builder.toString();
	}

}
